# -*- coding:utf-8 -*-

__author__='zhaoxp'


class LazyDB(object):
    def __init__(self):
        self.exits = 5

    def __getattr__(self,name):
        value = 'Value for %s'%name
        setattr(self,name,value)
        return value


class LoggingLazyDB(LazyDB):
    def __getattr__(self,name):
        print('Called __getattr__(%s)'%name)
        return super(LoggingLazyDB,self).__getattr__(name)


class ValidatingDB(object):
    def __init__(self):
        self.exists = 5

    def __getattribute__(self,name):
        print('Called __getattribute__(%s)'%name)
        try:
            return super(ValidatingDB,self).__getattribute__(name)
        except AttributeError:
            value = 'Value for %s'%name
            setattr(self,name,value)
            return value


class MissingPropertyDB(object):
    def __getattr__(self,name):
        if name == 'bad_name':
            raise AttributeError('%s is missing'%name)


class SavingDB(object):
    def __setattr__(self,name,value):
        # save some data to db log
        super(SavingDB,self).__setattr__(name,value)


class LoggingSavingDB(SavingDB):
    def __setattr__(self,name,value):
        print('Called __setattr__(%s,%s)'%(name,value))
        super(LoggingSavingDB,self).__setattr__(name,value)


class BrokenDictionaryDB(object):
    def __init__(self,data):
        self.__data = data

    def __getattribute__(self,name):
        print('Called __getattribute__(%s)'%name)
        return self._data[name]


class DictionaryDB(object):
    def __init__(self,data):
        self._data=data

    def __getattribute__(self,name):
        data_dict = super(DictionaryDB,self).__getattribute__('_data')
        return data_dict[name]


if __name__=='__main__':
    print('show usage of __getattr__ __getattribute__ __setattr__')
    data = LazyDB()
    print('before: ',data.__dict__)
    print('foo:    ',data.foo)
    print('after:  ',data.__dict__)
    
    print('-'*30)
    data = LoggingLazyDB()
    print('exists:',data.exists)
    print('foo:',data.foo)
    print('foo:',data.foo)
    
    print('-'*30)
    data = ValidatingDB()
    print('exists:',data.exists)
    print('foo:',data.foo)
    print('foo:',data.foo)
    
    print('-'*30)
    data = MissingPropertyDB()
    try:
        print('data.bad_name = %s'%data.bad_name)
    except AttributeError as ae:
        print('AttributeError --> %s'%ae)

    print('-'*30)
    data = LoggingLazyDB()
    print('before:    ',data.__dict__)
    print('foo exists:',hasattr(data,'foo'))
    print('after:     ',data.__dict__)
    print('foo exists:',hasattr(data,'foo'))

    print('-'*30)
    data = ValidatingDB()
    print('foo exists:',hasattr(data,'foo'))
    print('foo exists:',hasattr(data,'foo'))

    print('-'*30)
    data = LoggingSavingDB()
    print('before:  ',data.__dict__)
    data.foo = 5
    print('after:   ',data.__dict__)
    data.foo = 7
    print('finally: ',data.__dict__)
    print('-'*30)
    data = BrokenDictionaryDB({'foo':3})
    #print(data.foo) # RuntimeError: maximum recursion depth exceeded while calling a Python object
    
    print('-'*30)
    data = DictionaryDB({'foo':5})
    print('data.foo = %s'%data.foo)